// Copyright (c) 2020 Presto Labs Pte. Ltd.
// Author: jing

#pragma once

#include <string>
#include <string_view>

#include <rapidjson/document.h>
#include <rapidjson/rapidjson.h>

#include "coin/proto/coin_market_enums.pb.h"
#include "coin2/base/conversion.h"
#include "coin2/exchange/base/feed/update.h"
#include "coin2/exchange/base/feed_util/book_builder.h"
#include "coin2/exchange/base/feed_util/trade.h"

namespace impl {
using coin2::exchange::base::market::ExchangeType;
using coin2::exchange::base::market::MarketType;
using namespace std::literals;

class CoinoneFeedUpdateParser {
 public:
  static void ParseBook(BookBuilder* book_builder_, const rapidjson::Document& book_json) {
    auto ts = stoull(book_json["timestamp"].GetString()) * 1'000'000'000LL;
    auto tx = book_builder_->NewTransaction(ts);
    if (book_json["action"].GetString() == "SNAPSHOT"sv) {
      tx.Clear();
    }

    for (const auto& ask : book_json["ASK"].GetArray()) {
      tx.UpdateAsk(stod(ask["price"].GetString()), stod(ask["qty"].GetString()));
    }
    for (const auto& bid : book_json["BID"].GetArray()) {
      tx.UpdateBid(stod(bid["price"].GetString()), stod(bid["qty"].GetString()));
    }
  }

  static void ParseTrade(const rapidjson::Value& trade_json, Trade* trade) {
    trade->price = stod(trade_json["price"].GetString());
    trade->fill_qty = stod(trade_json["volume"].GetString());
    trade->side =
        ParseTradeSide(trade_json["is_ask"].GetString(), trade_json["is_seller_maker"].GetString());
    trade->timestamp = stoull(trade_json["timestamp"].GetString()) * 1'000'000'000LL;
  }

  static coin2::exchange::feed::TradeSide ParseTradeSide(
      const std::string& is_ask,
      const std::string& is_seller_maker) {
    if (is_seller_maker == "1") {
      if (is_ask != "1") {
        THROW() << "[Coinone]: 'is_seller_maker' and 'is_ask' mismatch";
      }
      return coin2::exchange::feed::TRADE_BUY_SIDE;
    } else if (is_seller_maker == "0") {
      if (is_ask != "0") {
        THROW() << "[Coinone]: 'is_seller_maker' and 'is_ask' mismatch";
      }
      return coin2::exchange::feed::TRADE_SELL_SIDE;
    } else {
      THROW() << "[Coinone] unknown side: " << is_seller_maker;
    }
    return coin2::exchange::feed::TRADE_NO_SIDE;
  }
};
}  // namespace impl

using impl::CoinoneFeedUpdateParser;
